package com.fub.positioning;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.fub.guidance.Point;

public class PositioningUtil {

	/**
	 * Constructs an label for reference points out of building id and position.
	 * 
	 * @param buildingID
	 *            the id of the building
	 * @param position
	 *            the position of the reference point
	 * @return
	 */
	public static String createLocationLabel(String buildingID, Point position) {
		String location = String.format("%s_%d_%d_%d", buildingID,
				position.getX(), position.getY(), position.getZ());
		return location;
	}

	/**
	 * Stops the thread.
	 * 
	 * @param time
	 *            the time in ms
	 */
	public static void sleep(int time) {
		try {
			Thread.sleep(time);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Calculates the average from many measurements.
	 * 
	 * @param measurements
	 *            list of measurements
	 * @return the average from many measurements
	 */
	public static Measurement getAverageFromManyMeasurements(
			List<Measurement> measurements) {
		Map<String, Integer> sumOfMeasurementsAccessPoint = new HashMap<String, Integer>();
		Measurement averageMeasurement = new Measurement();
		averageMeasurement = measurements.get(0);
		for (int i = 1; i < measurements.size(); i++) {
			Measurement currentMeasurement = measurements.get(i);
			for (String ssid : averageMeasurement.getSSids()) {
				if (currentMeasurement.getSingnalStrength(ssid) != -1) {
					averageMeasurement.putSignalStrength(
							ssid,
							averageMeasurement.getSingnalStrength(ssid)
									+ currentMeasurement
											.getSingnalStrength(ssid));
					if (sumOfMeasurementsAccessPoint.get(ssid) == null) {
						sumOfMeasurementsAccessPoint.put(ssid, 2);
					} else {
						sumOfMeasurementsAccessPoint.put(ssid,
								sumOfMeasurementsAccessPoint.get(ssid) + 1);
					}
				}
			}
		}
		for (String ssid : averageMeasurement.getSSids()) {
			int factor = 1;
			if (sumOfMeasurementsAccessPoint.get(ssid) != null) {
				factor = sumOfMeasurementsAccessPoint.get(ssid);
			}
			averageMeasurement.putSignalStrength(ssid,
					averageMeasurement.getSingnalStrength(ssid) / factor);
		}
		return averageMeasurement;
	}

	/**
	 * Calculates the manhatten distance of two points.
	 * 
	 * @param from
	 *            the first point
	 * @param to
	 *            the second point
	 * @return the manhatten distance of two points
	 */
	public static double getManhattenDistance(Measurement from, Measurement to) {
		double sum = 0;
		int sameSSIDs = 0;
		for (String ssid : from.getSSids()) {
			if (to.getSingnalStrength(ssid) != -1) {
				sum += Math.abs(from.getSingnalStrength(ssid)
						- to.getSingnalStrength(ssid));
				sameSSIDs++;
			} else {
				sum += 50;
				sameSSIDs++;
			}
		}
		if (sameSSIDs == 0) {
			sum = Double.MAX_VALUE;
		}
		return sum;
	}

	/**
	 * Calculates the euclidian distance of two points.
	 * 
	 * @param from
	 *            the first point
	 * @param to
	 *            the second point
	 * @return the euclidian distance of two points
	 */
	public static double getEuclidianDistance(Measurement from, Measurement to) {
		double sum = 0;
		int sameSSIDs = 0;
		for (String ssid : from.getSSids()) {
			if (to.getSingnalStrength(ssid) != -1) {
				int partSum = Math.abs(from.getSingnalStrength(ssid)
						- to.getSingnalStrength(ssid));
				sum += partSum * partSum;
				sameSSIDs++;
			} else {
				sum += 1000;
				sameSSIDs++;
			}
		}
		if (sameSSIDs == 0) {
			return Double.MAX_VALUE;
		}
		return sum / sameSSIDs;
	}

	/**
	 * Calculates the position from k nearest neighbors.
	 * 
	 * @param referencePoints
	 *            the nearest neighbors
	 * @param k
	 *            the k
	 * @return the calculated position
	 */
	public static int[] calculateKNNPoint(List<ReferencePoint> referencePoints,
			int k) {
		int sumPosX = 0;
		int sumPosY = 0;
		if (k > referencePoints.size()) {
			int[] result = { 0, 0 };
			return result;
		}
		// referencePoints = removeWorstOne(referencePoints);
		for (int i = 0; i < k; i++) {
			ReferencePoint nn = referencePoints.get(i);
			sumPosX += nn.getPosition().getX();
			sumPosY += nn.getPosition().getY();
		}
		if (k == 0) {
			k++;
		}
		int[] result = { sumPosX / (k), sumPosY / (k) };
		return result;
	}

	/**
	 * Calculates the position from k weighted nearest neighbors.
	 * 
	 * @param referencePoints
	 *            the nearest neighbors
	 * @param k
	 *            the k
	 * @return the calculated position
	 */
	public static int[] calculateKWNNPosition(
			List<ReferencePoint> referencePoints, int k) {
		float sumPosX = 0;
		float sumPosY = 0;
		float sumWeight = 0;

		if (k > referencePoints.size()) {
			int[] result = { 0, 0 };
			return result;
		}

		for (int i = 0; i < k; i++) {
			ReferencePoint nn = referencePoints.get(i);
			double inverseSignalDistance = 1 / nn
					.getDistanceToCurrentPosition();
			sumWeight += inverseSignalDistance;
			sumPosX += (inverseSignalDistance * nn.getPosition().getX());
			sumPosY += (inverseSignalDistance * nn.getPosition().getY());
		}
		int posX = (int) (sumPosX / sumWeight);
		int posY = (int) (sumPosY / sumWeight);
		int result[] = { posX, posY };
		return result;
	}
}
